package profile;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Locale;

import javax.imageio.ImageIO;

import jxl.CellReferenceHelper;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.write.Formula;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.NumberFormats;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableImage;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;

import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;

import util.ProfileChartHelper;

/**
 * This class takes in a Profile and the original image filename. It then
 * exports it to an excel (.xls) file using the JXL api
 */
public class ProfileExporter {

    // These are used to convert from pixel size to Excel row/column size
    public static int CHART_IMAGE_ROWS = 35;
    public static int CHART_IMAGE_COLS = 12;

    // Various formats used when writing to spreadsheet
    WritableCellFormat numberFormat;
    WritableCellFormat regularFormat;
    WritableCellFormat headerFormat;
    WritableCellFormat highlightedFormat;

    // Used to keep track of special columns and rows in the spreadsheet
    private int dipsCol = 2;
    private int minCol = 3;
    private int fwhmCol = 4;
    private int areaCol = 5;
    private int areaMeanCol = 6;
    private int areaElementsMeanCol = 7;
    private int numEltsCol = 2;
    private int numEltsRow = 0;

    private Profile profile;
    private String originalFilename;

    /**
     * Constructor takes in a profile and the original image file name
     * 
     * @param profile
     * @param originalFilename
     * @throws WriteException
     */
    public ProfileExporter(Profile profile, String originalFilename) throws WriteException {
        this.profile = profile;
        this.originalFilename = originalFilename;

        // setup cell formats
        WritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);
        numberFormat = new WritableCellFormat(NumberFormats.FLOAT);
        regularFormat = new WritableCellFormat(times10pt);
        highlightedFormat = new WritableCellFormat(NumberFormats.FLOAT);
        highlightedFormat.setBackground(Colour.YELLOW);
        headerFormat = new WritableCellFormat(times10pt);
        headerFormat.setBorder(Border.BOTTOM, BorderLineStyle.MEDIUM);
        headerFormat.setWrap(true);
    }

    /**
     * Exports the profile to an Excel spreadsheet. Takes in the current number
     * of MADs selected by the user, the file to write the spreadsheet to, and
     * the JFreeChart for the profile.
     * 
     * @param currentMad
     *            Current number of MADs selected by the user
     * @param file
     *            The Excel spreadsheet file
     * @param chart
     * @throws WriteException
     * @throws IOException
     */
    public void exportProfileToExcel(int currentMad, File file, JFreeChart chart) throws WriteException,
            IOException {
        WritableWorkbook workbook = createWorkbook(file);
        workbook.createSheet("Profile", 0);

        File chartImageFile = File.createTempFile("chartImg", ".png");
        ChartUtilities.writeChartAsPNG(new FileOutputStream(chartImageFile), chart, 800, 600);

        createContent(workbook, currentMad, chartImageFile);

        workbook.write();
        workbook.close();
    }

    /**
     * Creates workbook in file <code>file</code>
     * 
     * @param file
     * @return
     * @throws IOException
     */
    private WritableWorkbook createWorkbook(File file) throws IOException {
        WorkbookSettings wbSettings = new WorkbookSettings();
        wbSettings.setLocale(new Locale("en", "EN"));
        return Workbook.createWorkbook(file, wbSettings);
    }

    /**
     * Adds all of the data/statistics to the spreadsheet
     * 
     * @param workbook
     * @param currentMad
     * @param chartImageFile
     * @throws WriteException
     * @throws RowsExceededException
     * @throws IOException
     */
    private void createContent(WritableWorkbook workbook, int currentMad, File chartImageFile)
                                    throws WriteException, RowsExceededException, IOException {
        int row = 0;
        int col = 0;
        WritableSheet sheet = workbook.getSheet(0);
        for (int i = 0; i < profile.getArr().length; i++) {
            addNumber(profile.getArr()[i], col, i, sheet, numberFormat);
        }

        col = 2;
        addCaption("Filename:", col, row, sheet, regularFormat);
        addCaption(originalFilename, col + 1, row, sheet, regularFormat);

        double mad = profile.getProfileMAD();
        double mean = profile.getProfileMean();
        row += 2;
        addCaption("MAD is defined as the median of the absolute deviations from the data's median.", col, row, sheet,
                regularFormat);
        row++;
        addCaption("MAD", col, row, sheet, regularFormat);
        addNumber(mad, col + 1, row, sheet, numberFormat);
        row++;
        addCaption("Mean", col, row, sheet, regularFormat);
        addNumber(mean, col + 1, row, sheet, numberFormat);
        row++;
        addCaption("MAD/Mean", col, row, sheet, regularFormat);
        addNumber(mad / mean, col + 1, row, sheet, numberFormat);
        row++;

        // add peaks
        row++;
        addCaption("Number of elements:", numEltsCol, row, sheet, regularFormat);
        addCaption("<-- Enter number of elements in array", numEltsCol + 2, row, sheet, regularFormat);
        numEltsCol++;
        numEltsRow = row;

        row++;
        addCaption("Dips:", dipsCol, row, sheet, headerFormat);
        addCaption("Min", minCol, row, sheet, headerFormat);
        addCaption("Signal at half max", fwhmCol, row, sheet, headerFormat);
        addCaption("Area at FWHM", areaCol, row, sheet, headerFormat);
        addCaption("Area/mean", areaMeanCol, row, sheet, headerFormat);
        addCaption("Area in elements/mean", areaElementsMeanCol, row, sheet, headerFormat);

        row++;
        int peakNum = 1;
        for (Peak peak : profile.getPeaks(currentMad)) {
            addPeak(sheet, col, row, peak, peakNum);
            peakNum++;
            row++;
        }

        row++;

        addImages(sheet, row, currentMad, chartImageFile);
    }

    /**
     * Adds the original profile chart and original median image with the selection drawn
     * 
     * @param sheet
     * @param curRow
     * @param currentMad
     * @param chartImageFile
     * @throws IOException
     */
    private void addImages(WritableSheet sheet, int curRow, int currentMad, File chartImageFile) throws IOException {
        BufferedImage bimg = profile.getImageWithSelection();
        File profileImg = bufferedImageToFile(bimg);
        sheet.addImage(new WritableImage(2, curRow, CHART_IMAGE_COLS, CHART_IMAGE_ROWS, chartImageFile));
        sheet.addImage(new WritableImage(CHART_IMAGE_COLS + 4, curRow, bimg.getWidth() / 50, bimg.getHeight() / 12,
                profileImg));
        
        //We can delete the files once they are added to the spreadsheet
        profileImg.deleteOnExit();
        chartImageFile.deleteOnExit();

        curRow += CHART_IMAGE_ROWS + 5;
        addSmoothedProfileIimage(sheet, 2, curRow, currentMad);
    }

    /**
     * Adds an image of the smoothed profile to the spreadsheet
     * 
     * @param sheet
     * @param col
     * @param row
     * @param madNumber
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void addSmoothedProfileIimage(WritableSheet sheet, int col, int row, int madNumber)
            throws FileNotFoundException, IOException {
        // have to deep copy profile so it doesn't get overwritten after
        // applying srad
        double[] sradArr = new double[profile.arr.length];
        System.arraycopy(profile.arr, 0, sradArr, 0, sradArr.length);
        SradProfile sProfile = new SradProfile(sradArr, profile.getImageWithSelection());
        sProfile.applySrad();

        JFreeChart chart = ProfileChartHelper.getProfileChart(sProfile, "Smoothed Profile (OPTIONAL)", madNumber);
        File chartImageFile = File.createTempFile("chartImg", ".png");
        ChartUtilities.writeChartAsPNG(new FileOutputStream(chartImageFile), chart, 800, 600);

        sheet.addImage(new WritableImage(2, row + 7, CHART_IMAGE_COLS, CHART_IMAGE_ROWS, chartImageFile));

    }

    /**
     * Adds all of the calculations for each peak highlighted in the profile
     * 
     * @param sheet
     * @param col
     * @param row
     * @param peak
     * @param peakNum
     * @throws RowsExceededException
     * @throws WriteException
     */
    private void addPeak(WritableSheet sheet, int col, int row, Peak peak, int peakNum) throws RowsExceededException,
            WriteException {
        addNumber(peakNum, col, row, sheet, numberFormat);

        double mean = profile.getProfileMean();
        double signalAtFWHM = mean - (mean - peak.getMin()) / 2;
        double area = peak.getAreaUnderFWHM(signalAtFWHM, mean);
        addNumber(peak.getMin(), minCol, row, sheet, highlightedFormat);
        addNumber(signalAtFWHM, fwhmCol, row, sheet, highlightedFormat);
        addNumber(area, areaCol, row, sheet, highlightedFormat);
        addNumber(area / mean, areaMeanCol, row, sheet, numberFormat);
        addFormula((area / mean) + " * " + CellReferenceHelper.getColumnReference(numEltsCol) +
                  (numEltsRow + 1) + " / " + profile.getWidth(), areaElementsMeanCol, row, sheet, numberFormat);
    }

    /**
     * Takes in a BufferedImage and writes it to a temporary file.  This is used to add images
     * to the spreadsheet because the JXL API only allows adding image files and not
     * BufferedImages
     * 
     * @param bimg
     * @return
     * @throws IOException
     */
    private static File bufferedImageToFile(BufferedImage bimg) throws IOException {
        File outputfile = File.createTempFile("tmpbimg", ".png");
        ImageIO.write(bimg, "png", outputfile);
        return outputfile;
    }

    private static void addNumber(double d, int column, int row, WritableSheet sheet, WritableCellFormat format)
            throws WriteException, RowsExceededException {
        Number number;
        number = new Number(column, row, d, format);
        sheet.addCell(number);
    }

    private static void addCaption(String s, int column, int row, WritableSheet sheet, WritableCellFormat format)
            throws RowsExceededException, WriteException {
        Label label;
        label = new Label(column, row, s, format);
        sheet.addCell(label);
    }
    
    private void addFormula(String string, int col, int row, WritableSheet sheet,
            WritableCellFormat format) throws RowsExceededException, WriteException {
        Formula formula = new Formula(col, row, string, format);
        sheet.addCell(formula);
    }


}
